package com.zoffy.catdiarybackend.websocket;



import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.zoffy.catdiarybackend.common.IdRequest;
import com.zoffy.catdiarybackend.model.entity.Article;
import com.zoffy.catdiarybackend.model.vo.ArticleVo;
import com.zoffy.catdiarybackend.service.ArticleService;
import jakarta.annotation.Resource;
import org.springframework.web.socket.TextMessage;

import org.springframework.web.socket.WebSocketSession;


import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class ArticleCollaborationHandler {
    @Resource

    private final List<WebSocketSession>  clients=new ArrayList<>();
    private final List<OperationMessage<List<Operation>>> historyBuffer=new ArrayList<>();
    private StringBuffer content;
    private ArticleService articleService;
    private long articleId;
    public ArticleCollaborationHandler(){}
    public ArticleCollaborationHandler(long articleId, ArticleService articleService){
        this.articleId=articleId;
        this.articleService=articleService;
//        ArticleVo articleVo = articleService.getArticleVoById(new IdRequest(articleId));
//        content=new StringBuffer(articleVo.getContent());
    }

    public void handleMessage(TextMessage message){
        String messageStr=new String(message.asBytes());
        JSONObject jsonObject=JSONObject.parseObject(messageStr);
        OperationMessage<List<Operation>> operationMessage=new OperationMessage<>();
        operationMessage.setServerId((long) jsonObject.get("serverId"));
        operationMessage.setData(JSONArray.parseArray(jsonObject.get("operation").toString(),Operation.class));
        long serverId = operationMessage.getServerId();

        int i=0;
        for(;i<historyBuffer.size();i++){
            if(historyBuffer.get(i).getServerId()==serverId){
                break;
            }
        }
        for(;i< historyBuffer.size();i++){
            operationTransform(operationMessage,historyBuffer.get(i));
            operationMessage.setServerId(operationMessage.getServerId()+1);
        }
        execute(operationMessage);
        historyBuffer.add(operationMessage);
        broadcast(operationMessage);
    }

    private void OT_II(OperationMessage<List<Operation>> operationMessage,OperationMessage<List<Operation>> historyOperationMessage){
        Operation operation=operationMessage.getData().get(0);
        Operation historyOperation=historyOperationMessage.getData().get(0);
        int index = operation.getIndex();
        int historyOperationIndex = historyOperation.getIndex();
        //如果未同步插入下标是在在历史插入下标前，无需操作转换
        if(historyOperationIndex>=index){
            return;
        }
        //如果未同步插入下标是在在历史插入下标后，需操作转换
        operation.setIndex(index+historyOperation.getAdditionalMsg().length());

    }
    private void OT_ID(OperationMessage<List<Operation>> operationMessage,OperationMessage<List<Operation>> historyOperationMessage){
        Operation operation=operationMessage.getData().get(0);

        List<Operation> historyOperations = historyOperationMessage.getData();
        for(int i=historyOperations.size()-1;i>=0;i--){
            Operation historyOperation = historyOperations.get(i);
            int index = operation.getIndex();
            int historyOperationIndex = historyOperation.getIndex();
            int historyDeleteLength=Integer.parseInt(historyOperation.getAdditionalMsg());

            //如果未同步插入下标是在在历史删除下标前，无需操作转换
            if(historyOperationIndex>index){
                continue;
            }
            //如果未同步插入下标是在在历史删除下标后，需操作转换，Math.max(index-historyDeleteLength,historyOperationIndex)是为了尽量保证操作意图
            operation.setIndex(Math.max(index-historyDeleteLength,historyOperationIndex));
        }




    }
    private void OT_DI(OperationMessage<List<Operation>> operationMessage,OperationMessage<List<Operation>> historyOperationMessage){
        Operation historyOperation = historyOperationMessage.getData().get(0);
        List<Operation> operations = operationMessage.getData();
        int historyOperationIndex = historyOperation.getIndex();
        int historyInsertLength=historyOperation.getAdditionalMsg().length();
        List<Operation> newOperations=new ArrayList<>();
        for(Operation operation:operations){
            int index = operation.getIndex();
            int deleteLength=Integer.parseInt(operation.getAdditionalMsg());
            if(index+deleteLength+1<=historyOperationIndex){
                newOperations.add(operation);
                continue;
            }
            if(historyOperationIndex<index){
                operation.setIndex(index+historyInsertLength);
                newOperations.add(operation);
                continue;
            }
            //历史插入操作发生在未同步删除下标区间可以考虑拆分为两条操作
            Operation operation1=new Operation();
            Operation operation2=new Operation();
            operation1.setIndex(index);
            operation1.setAdditionalMsg(String.valueOf(historyOperationIndex-index+1));
            operation2.setIndex(historyOperationIndex+historyInsertLength+1);
            operation2.setAdditionalMsg(String.valueOf(deleteLength-(historyOperationIndex-index+1)));
            newOperations.add(operation1);
            newOperations.add(operation2);
        }
        operationMessage.setData(newOperations);
    }
    private void OT_DD(OperationMessage<List<Operation>> operationMessage,OperationMessage<List<Operation>> historyOperationMessage){
        List<Operation> operations = operationMessage.getData();
        List<Operation> historyOperations = historyOperationMessage.getData();
        List<Operation> newOperations=new ArrayList<>();

        for (Operation operation:operations){
            int index = operation.getIndex();
            int deleteLength = Integer.parseInt(operation.getAdditionalMsg());
            int pre=index;
            int offset=0;
            for(Operation historyOperation:historyOperations){
                int historyIndex = historyOperation.getIndex();
                int historyDeleteLength = Integer.parseInt(historyOperation.getAdditionalMsg());

                //历史操作包含未同步操作则无需转换
                if(historyIndex<=index && (historyIndex+historyDeleteLength)>=(index+deleteLength)){
                    //这里以及之下更新pre是为了防止触发循环外pre<=index+deleteLength的if语句
                    pre=Math.max(index,historyIndex+historyDeleteLength);
                    break;
                }
                //在未同步删除操作后的历史删除操作不会对意图产生影响，无需转换
                if(historyIndex>=index+deleteLength){
                    break;
                }
                if(historyIndex>index){
                    Operation newOperation=new Operation();
                    newOperation.setIndex(pre-offset);
                    newOperation.setAdditionalMsg(String.valueOf(historyIndex-pre));
                    newOperations.add(newOperation);
                }
                offset+=historyDeleteLength;
                pre=Math.max(index,historyIndex+historyDeleteLength);
            }
            if(pre<=index+deleteLength){
                Operation newOperation=new Operation();
                newOperation.setIndex(pre-offset);
                newOperation.setAdditionalMsg(String.valueOf(index+deleteLength-pre));
                newOperations.add(newOperation);
            }
        }

        operationMessage.setData(newOperations);

    }
    public void addClient(WebSocketSession session){
        clients.add(session);
    }
    public void removeClient(WebSocketSession session){
        clients.remove(session);
    }
    private void execute(OperationMessage<List<Operation>> operationMessage){
        String type = operationMessage.getType();
        List<Operation> operations = operationMessage.getData();

        for(Operation operation:operations){

            int index = operation.getIndex();
            String additionalMsg = operation.getAdditionalMsg();

            if("insert".equals(type)){
                content.insert(index,additionalMsg);
            }else if("delete".equals(type)){
                content.delete(index,index+Integer.parseInt(additionalMsg));
            }
        }
    }

    private void operationTransform(OperationMessage<List<Operation>> operationMessage,OperationMessage<List<Operation>> historyOperationMessage){
        String type1=operationMessage.getType();
        String type2= historyOperationMessage.getType();
        if("insert".equals(type1)&&"insert".equals(type2)){
            OT_II(operationMessage,historyOperationMessage);
        }else if("insert".equals(type1)&&"delete".equals(type2)){
            OT_ID(operationMessage,historyOperationMessage);
        } else if ("delete".equals(type1)&&"insert".equals(type2)) {
            OT_DI(operationMessage,historyOperationMessage);
        }else if ("delete".equals(type1)&&"delete".equals(type2)) {
            OT_DD(operationMessage,historyOperationMessage);
        }
    }
    private void broadcast(OperationMessage<List<Operation>> operationMessage){
        TextMessage message=new TextMessage(JSON.toJSONString(operationMessage));
        for(WebSocketSession session:clients){
            try {
                session.sendMessage(message);
            } catch (IOException e){}

        }
    }
    private void persist(){
        String contentStr = content.toString();
        Article article=new Article();
        article.setId(articleId);
        article.setContent(contentStr);
        articleService.updateById(article);

    }

}
